package controls.throbber
{

   // This class implements a throbber.

   // Set DEGREES_PER_TICK to slightly less than 360/BALL_COUNT and you will
   // get an effect of the ring rotating slowly counterclockwise while balls
   // grow rapidly in turn clockwise. Set DEGREES_PER_TICK to a small positive
   // or negative number to get a less busy ring that rotates clockwise or
   // counterclockwise, respectively.

   // You can also get an interesting effect by removing the Timer
   // infrastructure and instead using callLater(this.tick) in tick(). This
   // results in a rather spastic throbber that behaves differently at
   // different times.

   import flash.display.Graphics;
   import flash.events.TimerEvent;
   import flash.utils.Timer;
   
   import mx.core.UIComponent;

	public class Throbber extends UIComponent {

      // ---------------- Config Variables -------------- //
      protected static const TIMER_INTERVAL:Number = (1000.0/24);
      protected static const DEGREES_PER_TICK:int = 16;
      protected static const BALL_COUNT:int = 15;
      protected static const BALL_RADIUS_SMALL:Number = 3;
      protected static const BALL_RADIUS_LARGE:Number = 8;
      protected static const CIRCLE_RADIUS:int = 40;
      protected static const BALL_COLOR:int = 0x000000; 
      // ---------------- END Config Variables -------------- //

		public var isRunning:Boolean = false;

      // ---------------- Protected Variables --------------- //
      protected var timer:Timer;
      // ---------------- END Protected Variables --------------- //

      // ----------------- Initialization and Setup ---------------- //
      public function Throbber()
      {
         this.timer = new Timer(TIMER_INTERVAL);
         this.timer.addEventListener(TimerEvent.TIMER, this.tick,
                                     false, 0, true);

         this.draw();
      }
      // ----------------- Initialization and Setup ---------------- //

      // --------------- Accessors --------------- //
      override public function get measuredHeight() :Number
      {
         return 2 * (CIRCLE_RADIUS + BALL_RADIUS_LARGE);
      }

      override public function get measuredWidth() :Number
      {
         return height;
      }
      // --------------- Accessors --------------- //

      // --------------- Throbber Functions ---------------- //
      protected function draw() :void
      {
         var gr:Graphics = this.graphics;

         gr.beginFill(BALL_COLOR);
         gr.drawCircle(CIRCLE_RADIUS, 0, BALL_RADIUS_LARGE);
         gr.endFill();
         
         var increment:Number = (BALL_RADIUS_LARGE - BALL_RADIUS_SMALL) / BALL_COUNT;
         
         for (var i:int = 1; i < BALL_COUNT; i++) 
         {
            var r:Number = i * (2 * Math.PI / BALL_COUNT);
            var x:Number = Math.cos(r) * CIRCLE_RADIUS;
            var y:Number = Math.sin(r) * CIRCLE_RADIUS;
            gr.beginFill(BALL_COLOR, 1 - i / BALL_COUNT);
            gr.drawCircle(x, y, BALL_RADIUS_LARGE - increment * i);
         }
         gr.endFill();
      }

      public function start() :void
      {
         this.timer.start();
         isRunning = true;
      }

      public function stop() :void
      {
         this.timer.stop();
         isRunning = false;
      }

      protected function tick(ev:TimerEvent) :void
      {
      	this.rotation += DEGREES_PER_TICK;
      }
      // --------------- END Throbber Functions ---------------- //
   }
}
